package com.huatai.datacenter.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * @author Lion
 * @date 2023/7/10  15:25
 */
@Slf4j
@Component
@Order(1)
@Lazy(value = false)
public class ApplicationContextUtil implements ApplicationContextAware {

	/**
	 * 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候中取出ApplicaitonContext,需要所有bean注入完毕后，才能获取content
	 */
	private static ApplicationContext context;

	private static void assertApplicationContext() {
		if (context == null) {
			// 如果需要使用上下文，可以通过@PostConstruct 、实现CommandLineRunner接口并重写run()方法、使用ContextRefreshedEvent事件(上下文件刷新事件)三种方式
			// 详见：如何在Springboot服务启动之后执行一些代码(三种方式)
			throw new RuntimeException("ApplicationContext为null,请检查是否注入了SpringContextHolder，如果未注入，可以在bean中添加注解@DependsOn(\"applicationContextUtil\")!");
		}
	}

	/**
	 * 所有的Bean注入完毕才会执行
	 *
	 * @param applicationContext
	 * @throws BeansException
	 */
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		if (context != null) {
			log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + context);
		}
		context = applicationContext;
	}

	public static ApplicationContext getContext() {
		return context;
	}

	/**
	 * * 获取类型为requiredType的对象
	 *
	 * @param requiredType 对象类型
	 * @return Object 返回requiredType类型对象
	 * @throws BeansException
	 */
	public final static <T> T getBean(Class<T> requiredType) {
		assertApplicationContext();
		return context.getBean(requiredType);
	}

	/**
	 *
	 * 获取对象
	 *
	 * @param beanName
	 * @return Object 一个以所给名字注册的bean的实例
	 * @throws BeansException
	 */
	public final static Object getBean(String beanName) {
		assertApplicationContext();
		return context.getBean(beanName);
	}

	/**
	 * 获取类型为requiredType的对象
	 * 如果bean不能被类型转换，相应的异常将会被抛出（BeanNotOfRequiredTypeException）
	 *
	 * @param beanName bean注册名
	 * @param requiredType   返回对象类型
	 * @return Object 返回requiredType类型对象
	 * @throws BeansException
	 */
	public final static Object getBean(String beanName, Class<?> requiredType) {
		assertApplicationContext();
		return context.getBean(beanName, requiredType);
	}

	/**
	 * 获取类型为T的所有bean，主要用于根据基类获取所有的子类
	 *
	 * @param requiredType
	 * @param <T>
	 * @return
	 */
	public final static <T> Map<String, T> getBeans(Class<T> requiredType) {
		assertApplicationContext();
		return context.getBeansOfType(requiredType);
	}

	/**
	 * 将jvm中的class注册至spring的bean容器中；
	 * 注入之后，可以通过 @Autowired 方式获取bean对象
	 *
	 * @param clazzs
	 */
	public final static void registerBeans(List<Class<?>> clazzs) {
		ConfigurableApplicationContext context = (ConfigurableApplicationContext) getContext();
		DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();
		//逐个判断class，将满足条件的class添加至spring 的容器中进行管理
		for (Class<?> clazz : clazzs) {
			if (clazz.isInterface()) {
				break;
			}
			// 有@Service，@Commponent注解的，添加至bean容器管理
			BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
			//一定要注意注册bean是的beanName， 先取@Commponetn中的value，如果没有，再取类名且首字母小写
			beanFactory.registerBeanDefinition(clazz.getSimpleName(), beanDefinitionBuilder.getRawBeanDefinition());
		}
	}

	/**
	 * 获取类型为T的所有bean，主要用于基类或接口的全部实现类
	 *
	 * @param annotationType
	 * @param <T>
	 * @return
	 */
	public final static Map getBeansWithAnnotation(Class<? extends Annotation> annotationType) {
		assertApplicationContext();
		return context.getBeansWithAnnotation(annotationType);
	}

	/**
	 *
	 * 如果BeanFactory包含一个与所给名称匹配的bean定义，则返回true
	 *
	 * @param name
	 * @return boolean
	 *
	 */
	public static boolean containsBean(String name) {
		assertApplicationContext();
		return context.containsBean(name);
	}

	/**
	 *
	 * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。
	 * 如果与给定名字相应的bean定义没有被找到，将会抛出一个异常（NoSuchBeanDefinitionException）
	 *
	 * @param name
	 * @return boolean
	 * @throws NoSuchBeanDefinitionException
	 */

	public static boolean isSingleton(String name)
		throws NoSuchBeanDefinitionException {
		assertApplicationContext();
		return context.isSingleton(name);
	}

	/**
	 * @param name
	 * @return Class 注册对象的类型
	 * @throws NoSuchBeanDefinitionException
	 */

	public static Class getType(String name) throws NoSuchBeanDefinitionException {
		assertApplicationContext();
		return context.getType(name);
	}

	/**
	 *
	 * 如果给定的bean名字在bean定义中有别名，则返回这些别名
	 *
	 * @param name
	 * @throws NoSuchBeanDefinitionException
	 * @return
	 */

	public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
		assertApplicationContext();
		return context.getAliases(name);
	}

	/**
	 * 国际化使用
	 *
	 * @param key
	 * @return
	 */
	public static String getMessage(String key) {
		return context.getMessage(key, null, Locale.getDefault());
	}

	/**
	 * 获取当前环境
	 *
	 * @return
	 */
	public static String getActiveProfile() {
		return context.getEnvironment().getActiveProfiles()[0];
	}
}
